#include <stdio.h>
#include <assert.h>
#include <string.h>

#include "com_facesdk_FaceFeatureProcessor.h"
#include "imageprocess.h"
#include "getfacefeature.h"
#include "base64.h"
#include "FeatureCompare.h"
#include <android/log.h>
#define  LOG_TAG    "gd_feature:"
#define  LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

#define STANDARD_SIZE 35*35
#define RTN_SIZE 210

ImageProcess image_process;
GetFaceFeature get_face_feature;

static bool init_ff_status=false;

JNIEXPORT void JNICALL Java_com_facesdk_FaceFeatureProcessor_FaceFeatureInit
  (JNIEnv *, jclass)
{
    LOGD("initializing FD Feature......\n");
    fflush(stdout);
    if(image_process.Process_Initial()!=1||get_face_feature.Initialize(2<<8|1)!=1){
        init_ff_status=false;
        LOGE("init FD Feature failed.\n");
        return;
    }
    init_ff_status=true;
    LOGD("init FD Feature completed.\n");
    fflush(stdout);
}


JNIEXPORT void JNICALL Java_com_facesdk_FaceFeatureProcessor_FaceFeatureUninit
  (JNIEnv *, jclass)
{
    //destroy_face_feature_arr();
}

JNIEXPORT jbyteArray JNICALL Java_com_facesdk_FaceFeatureProcessor_GetFeatureCode
  (JNIEnv *env, jclass, jbyteArray image_data, jint w, jint h, jint lx,
   jint ly, jint rx, jint ry)
{
    if(!init_ff_status||image_data==NULL) {
	LOGE("GetFeatureCode: status or data null");
        return NULL;
    }
    jbyte *data=env->GetByteArrayElements(image_data,NULL);


    unsigned char standard_image[STANDARD_SIZE];
    memset(standard_image,0,sizeof(char)*STANDARD_SIZE);
    int ret=image_process.Process_WholeImage((unsigned char *)data,w,h,lx,ly,rx,ry,standard_image);

    env->ReleaseByteArrayElements(image_data,data,JNI_ABORT);


    if(ret!=1){
        LOGE("Process whole image failed.\n");
        return NULL;
    }

    float feature_used[RTN_SIZE];
    memset(feature_used,0,sizeof(float)*RTN_SIZE);
    ret=get_face_feature.GetFeatureCode(standard_image,feature_used);
    if(ret!=1){
        LOGE("Get feature code failed.\n");
        return NULL;
    }

    jbyteArray jb_arr=env->NewByteArray(RTN_SIZE*sizeof(float));
    if(jb_arr==NULL){
        LOGE("jnienv new byte array failed.\n");
        return NULL;
    }
    env->SetByteArrayRegion(jb_arr,0,RTN_SIZE*sizeof(float),(jbyte *)feature_used);

#ifndef NDEBUG
    for(int i=0;i<210;i++)
        printf("%f ",feature_used[i]);
    printf("\n");
#endif
    return jb_arr;
}

JNIEXPORT jbyteArray JNICALL Java_com_facesdk_FaceFeatureProcessor_GetFeatureCodeV2
  (JNIEnv *env, jclass, jbyteArray image_data, jint w, jint h, jint lx,
   jint ly, jint rx, jint ry)
{
    if(!init_ff_status||image_data==NULL) {
	LOGE("GetFeatureCodeV2: status or data null");
        return NULL;
    }

    jbyte *data=env->GetByteArrayElements(image_data,NULL);


    unsigned char standard_image[STANDARD_SIZE];
    memset(standard_image,0,sizeof(char)*STANDARD_SIZE);
    int ret=image_process.Process_WholeImage((unsigned char *)data,w,h,lx,ly,rx,ry,standard_image);

    env->ReleaseByteArrayElements(image_data,data,JNI_ABORT);


    if(ret!=1){
        LOGE("GetFeatureCodeV2:Process whole image failed.\n");
        return NULL;
    }

    float feature_used[RTN_SIZE+2];

	 assert(sizeof(float)*2==sizeof(double));

    memset(feature_used,0,sizeof(float)*(RTN_SIZE+2));
    ret=get_face_feature.GetFeatureCode(standard_image,feature_used);
    if(ret!=1){
        LOGE("GetFeatureCodeV2:Get feature code failed.\n");
        return NULL;
    	}
	 
	double precomp=Dot_Product1(feature_used,feature_used,RTN_SIZE);
	LOGD("GetFeatureCodeV2:precomp:%lf\n",precomp);
#ifndef NDEBUG
	printf("precomp:%lf\n",precomp);
#endif
	*(double *)(feature_used+RTN_SIZE)=precomp;

    jbyteArray jb_arr=env->NewByteArray((RTN_SIZE+2)*sizeof(float));
    if(jb_arr==NULL){
        LOGE("GetFeatureCodeV2:jnienv new byte array failed.\n");
        return NULL;
    	}
    env->SetByteArrayRegion(jb_arr,0,(RTN_SIZE+2)*sizeof(float),(jbyte *)feature_used);

#ifndef NDEBUG
    for(int i=0;i<210;i++)
        printf("%f ",feature_used[i]);
    printf("\n");
#endif
    return jb_arr;
}

JNIEXPORT jstring JNICALL Java_com_facesdk_FaceFeatureProcessor_GetFeatureCodeB64
(JNIEnv *env, jclass, jbyteArray image_data, jint w, jint h, jint lx,
 jint ly, jint rx, jint ry)
{
    if(!init_ff_status||image_data==NULL) {
	LOGE("GetFeatureCodeB64: status or data null");
        return NULL;
    }

    jbyte *data=env->GetByteArrayElements(image_data,NULL);

    unsigned char standard_image[STANDARD_SIZE];
    memset(standard_image,0,sizeof(char)*STANDARD_SIZE);
    int ret=image_process.Process_WholeImage((unsigned char *)data,w,h,lx,ly,rx,ry,standard_image);

    env->ReleaseByteArrayElements(image_data,data,JNI_ABORT);

    if(ret!=1){
        LOGE("Process whole image failed.\n");
        return NULL;
    }

    float feature_used[RTN_SIZE];
    memset(feature_used,0,sizeof(float)*RTN_SIZE);
    ret=get_face_feature.GetFeatureCode(standard_image,feature_used);

    if(ret!=1){
        LOGE("Get feature code failed.\n");
        return NULL;
    }

    char *base64=(char *)malloc(sizeof(float)*(RTN_SIZE/3)*4+4);//sizeof(float)*210/3*4
    if(base64==NULL){
        LOGE("malloc %u for base64 str failed.\n",sizeof(float)*(RTN_SIZE/3)*4+4);
        return NULL;
    }
#ifndef NDEBUG
    for(int i=0;i<210;i++)
        printf("%f ",feature_used[i]);
    printf("\n");
#endif
    base64_encode((unsigned char *)feature_used,base64,sizeof(float)*RTN_SIZE);

    jstring rstr=env->NewStringUTF(base64);
    free(base64);
    return rstr;
}

JNIEXPORT jstring JNICALL Java_com_facesdk_FaceFeatureProcessor_GetFeatureCodeB64V2
(JNIEnv *env, jclass, jbyteArray image_data, jint w, jint h, jint lx,
 jint ly, jint rx, jint ry)
{
    if(!init_ff_status||image_data==NULL) {
	LOGE("GetFeatureCodeB64V2: status or data null");
        return NULL;
    }

    jbyte *data=env->GetByteArrayElements(image_data,NULL);


    unsigned char standard_image[STANDARD_SIZE];
    memset(standard_image,0,sizeof(char)*STANDARD_SIZE);
    int ret=image_process.Process_WholeImage((unsigned char *)data,w,h,lx,ly,rx,ry,standard_image);

    env->ReleaseByteArrayElements(image_data,data,JNI_ABORT);


    if(ret!=1){
        LOGE("Process whole image failed.\n");
        return NULL;
    }

    float feature_used[RTN_SIZE+2];

	 assert(sizeof(float)*2==sizeof(double));

    memset(feature_used,0,sizeof(float)*(RTN_SIZE+2));
    ret=get_face_feature.GetFeatureCode(standard_image,feature_used);
    if(ret!=1){
        LOGE("Get feature code failed.\n");
        return NULL;
    	}

	double precomp=Dot_Product1(feature_used,feature_used,RTN_SIZE);
	LOGD("precomp:%lf\n",precomp);
#ifndef NDEBUG
	printf("precomp:%lf\n",precomp);
	printf("malloc size:%d\n",sizeof(float)*(((RTN_SIZE+2)/3)*4+4));
#endif
	*(double *)(feature_used+RTN_SIZE)=precomp;

    char *base64=(char *)malloc(sizeof(float)*(((RTN_SIZE+2)/3)*4+4));//sizeof(float)*210/3*4
    if(base64==NULL){
        LOGE("malloc %u for base64 str failed.\n",sizeof(float)*((RTN_SIZE+2)/3)*4+4);
        return NULL;
    }
#ifndef NDEBUG
    for(int i=0;i<210;i++)
        printf("%f ",feature_used[i]);
    printf("\n");
#endif
    base64_encode((unsigned char *)feature_used,base64,sizeof(float)*(RTN_SIZE+2));

	 //free(base64);

    jstring rstr=env->NewStringUTF(base64);
    free(base64);
    return rstr;
}

JNIEXPORT jdouble JNICALL Java_com_facesdk_FaceFeatureProcessor_CompareCode
  (JNIEnv *env, jclass, jbyteArray feature_a, jbyteArray feature_b, jint flag)
{
    if(!init_ff_status||feature_a==NULL||feature_b==NULL)
        return -1.0;

    jbyte *ba=env->GetByteArrayElements(feature_a,NULL);
    jbyte *bb=env->GetByteArrayElements(feature_b,NULL);
    double similarity=0.0;
    //int ret=get_face_feature.CompareCode((unsigned char *)ba,(unsigned char *)bb,&similarity,(FRVectorDistanceType)flag);
	CompareVectors((unsigned char *)ba,(unsigned char *)bb,&similarity);
	//int ret=1;

    env->ReleaseByteArrayElements(feature_a,ba,JNI_ABORT);
    env->ReleaseByteArrayElements(feature_b,bb,JNI_ABORT);


    //if(ret!=1){
       // fprintf(stderr,"compare code failed.\n");
       // return -1.0;
    //}
    LOGD("similarity v2 :c++:%lf.\n",similarity);
#ifndef NDEBUG
    printf("similarity v2 :c++:%lf.\n",similarity);
#endif
    return similarity;
}

JNIEXPORT jdouble JNICALL Java_com_facesdk_FaceFeatureProcessor_CompareCodeV2
  (JNIEnv *env, jclass, jbyteArray feature_a, jbyteArray feature_b, jint flag)
{
    double similarity,precompA,precompB;

    if(!init_ff_status||feature_a==NULL||feature_b==NULL)
        return -1.0;

    jbyte *ba=env->GetByteArrayElements(feature_a,NULL);
    jbyte *bb=env->GetByteArrayElements(feature_b,NULL);
	 
	 //similarity=0.0;
	 precompA=*((double *)(((float *)ba)+RTN_SIZE));
	 precompB=*((double *)(((float *)bb)+RTN_SIZE));
	LOGD("precompA %lf,precompB %lf\n",precompA,precompB);
#ifndef 	NDEBUG
	printf("precompA %lf,precompB %lf\n",precompA,precompB);
#endif
	 CompareVectors((const unsigned char *)ba,(const unsigned char*)bb,precompA,precompB,&similarity);

    env->ReleaseByteArrayElements(feature_a,ba,JNI_ABORT);
    env->ReleaseByteArrayElements(feature_b,bb,JNI_ABORT);


  //  if(ret!=1){
    //    fprintf(stderr,"compare code failed.\n");
      //  return -1.0;
    //}
    LOGD("similarity v2 :c++:%lf.\n",similarity);
#ifndef NDEBUG
    printf("similarity v2 :c++:%lf.\n",similarity);
#endif
    return similarity;
}

JNIEXPORT jbyteArray JNICALL Java_com_facesdk_FaceFeatureProcessor_ConvertCode
  (JNIEnv *env, jclass, jbyteArray featureV1)
{
	if(!init_ff_status||featureV1==NULL)
        return NULL;
	jbyte *p=env->GetByteArrayElements(featureV1,NULL);
	double precomp=Dot_Product1((float *)p,(float *)p,RTN_SIZE);

	float feature_used[RTN_SIZE+2];
	memcpy(feature_used,p,sizeof(float)*RTN_SIZE);
	*(double *)(feature_used+RTN_SIZE)=precomp;
	
	env->ReleaseByteArrayElements(featureV1,p,JNI_ABORT);

	jbyteArray jb_arr=env->NewByteArray((RTN_SIZE+2)*sizeof(float));
    if(jb_arr==NULL){
        LOGE("jnienv new byte array failed.\n");
        return NULL;
    	}
   env->SetByteArrayRegion(jb_arr,0,(RTN_SIZE+2)*sizeof(float),(jbyte *)feature_used);
	return jb_arr;
}

